package hamaster.jpbc.pbs;

import static hamaster.jpbc.pbc.PBC.PBC_ZR_LENGTH;
import hamaster.jpbc.pbc.CipherText;
import hamaster.jpbc.pbc.PBC;
import hamaster.jpbc.pbc.PlainText;
import hamaster.jpbc.pbc.PrivateKey;
import hamaster.jpbc.pbc.PublicParameter;
import hamaster.jpbc.util.Hash;
import it.unisa.dia.gas.jpbc.Element;
import it.unisa.dia.gas.jpbc.Pairing;

import java.util.Arrays;

public class PBS {

	private PBC pbc;
	private static PBS singleton;

	static {
		if (singleton == null)
			singleton = new PBS();
	}

	private PBS() {
		pbc = null;
	}

	public static PBS instance() {
		if (singleton.pbc == null)
			return null;
		return singleton;
	}
	public static PBS instance(PBC pbc) {
		if (singleton.pbc == null)
			singleton.pbc = pbc;
		return singleton;
	}

	/**
	 * 生成个人参数
	 * @param masterKey 个人签名主密钥 用于保存随机的主密钥
	 * @param email 电子邮件地址
	 * @return 个人参数
	 */
	public PersonalParameter genernate(Element masterKey, String email) {
		PublicParameter parameter = pbc.setup(masterKey);
		PersonalParameter id = new PersonalParameter();
		id.setG(parameter.getG());
		id.setG1(parameter.getG1());
		id.setH(parameter.getH());
		Element rID = pbc.getPairing().getZr().newElement();
		rID.setFromBytes(Hash.sha1(email));
		id.setrID(rID);
		id.setrIDString(email);
		return id;
	}

	public Signature sign(PersonalParameter id, Element masterKey, byte[] hash) {
		if (hash.length > PBC_ZR_LENGTH)
			throw new IllegalArgumentException("hash length too long:" + hash.length);
		Element user = pbc.getPairing().getZr().newElement();
		byte[] hash0 = new byte[PBC_ZR_LENGTH];
		Arrays.fill(hash0, (byte) 0);
		System.arraycopy(hash, 0, hash0, PBC_ZR_LENGTH - hash.length, hash.length);
		user.setFromBytes(hash0);
		PrivateKey pk = pbc.keygen(user, id.getUser(), masterKey, id);
		Signature signature = new Signature();
		signature.setHash(hash0);
		signature.setHashLength(hash.length);
		signature.sethID(pk.gethID());
		signature.setrID(pk.getrID());
		return signature;
	}

	public boolean verify(Signature signature, PersonalParameter parameter) {
		Element randomMessage = pbc.getPairing().getGT().newRandomElement();
		Element hash = pbc.getPairing().getZr().newElement();
		hash.setFromBytes(signature.getHash());
		PlainText p = new PlainText();
		p.setElement(randomMessage);
		p.setLength(128);
		CipherText cipher = pbc.encrypt(p, hash, parameter);
		PlainText plain = pbc.decrypt(cipher, signature);
		return p.equals(plain);
	}

	public Pairing getPairing() {
		return pbc.getPairing();
	}
}
